class Loader {
    public static loadedScripts: { [s: string]: any } = {
        "console-manage.js": Date.now
    };

    public static LoadScript(addr: string, callback: () => void): boolean {
        if (Loader.loadedScripts[addr] != null) {
            callback();
            return false;
        }
        var scrEl = <HTMLScriptElement>document.createElement("script");
        scrEl.type = "text/javascript";
        scrEl.onload = e => {
            Loader.loadedScripts[addr] = Date.now;
            callback();
        };
        scrEl.src = addr;
        document.head.appendChild(scrEl);
    }
}

class Pjs2Loader {
    public static LoadDefinitons(addr: string, rnt: Pjs2.Runtime) {
        $.ajax({
            url: addr,
            success: d => {
                if (typeof d != "string") {
                    d = JSON.stringify(d);
                }
                Pjs2Loader.LoadDefs(d, rnt);
            },
            error: e => {
                if (e.status == 200)
                    Pjs2Loader.LoadDefs(e.responseText, rnt);
            }
        });
    }

    private static LoadDefs(json: string, rnt: Pjs2.Runtime) {
        var d = $.parseJSON(json);
        console.log(json);
        (<any[]>d["functions"]).forEach(f => {
            if (f["definedIn"] && f["name"]) {
                var name = <string>f["name"];
                var funcName = <string>f["funcName"];
                if (!funcName) {
                    funcName = name.replace(/\-(.)/, (a, b) => (<string>b).toUpperCase());
                    funcName = funcName.charAt(0).toLowerCase() + funcName.substr(1);
                }
                var defin = <string>f["definedIn"];
                var defin = <string>d["fileAliases"][defin] || defin;
                var funcDef = new Pjs2.FunctionDefinition();
                funcDef.name = name;
                funcDef.doFunc = this.GetLazyDoFunction(defin, funcName, funcDef);
                funcDef.parameters = this.GetParameters(f["parameters"]);
                rnt.AddFunction(funcDef);
            }
        });
        if (d["aliases"] && d["aliases"] instanceof Array)
            (<any[]>d["aliases"]).forEach(a => {
                if (a.name && a.for)
                    rnt.AddAlias(a.name, a.for);
            });
    }

    private static GetParameters(a: Array<any>): Pjs2.FuncParameterDefinition[] {
        var res = new Array<Pjs2.FuncParameterDefinition>();
        a.forEach(p => {
            var r = new Pjs2.FuncParameterDefinition(
                p["name"],
                !!p["required"],
                !!p["allowPipeline"]);
            res.push(r);
        });
        return res;
    }

    private static GetLazyDoFunction(definedIn: string, funcName: string, definition: Pjs2.FunctionDefinition):
        (r: Pjs2.Runtime, param: any) => any {
        return (r: Pjs2.Runtime, p: any) => {
            if (!Loader.loadedScripts[definedIn]) {
                var callback = new Pjs2.ReturnCallback();
                callback.PostMessage("Loading script " + definedIn)
                Loader.LoadScript(definedIn, () => {
                    var f = this.GetFuncByName(funcName);
                    definition.doFunc = (r1, p1) => f.call(null, r1, p1);
                    callback.Return(definition.Call(r, p));
                });
                return callback;
            }
            else {
                var f = this.GetFuncByName(funcName);
                definition.doFunc = (r1, p1) => f.call(null, r1, p1);
                return definition.Call(r, p);
            }
        };
    }

    private static GetFuncByName(name: string): Function {
        return window[name];
    }
}